﻿Imports System
Imports System.Collections.Generic
Imports System.Diagnostics
Imports System.Reflection
Imports System.Workflow.ComponentModel
Imports System.Workflow.Runtime

Namespace System.Workflow.Activities
    Friend Class CorrelationService
        ' Methods
        Private Shared Sub CreateFollowerEntry(ByVal context As IServiceProvider, ByVal interfaceType As Type, ByVal followermethodName As String, ByVal initializermethodName As String)
            If CorrelationResolver.IsInitializingMember(interfaceType, initializermethodName, Nothing) Then
                Dim service As WorkflowQueuingService = DirectCast(context.GetService(GetType(WorkflowQueuingService)), WorkflowQueuingService)
                Dim eventListener As New FollowerQueueCreator(followermethodName)
                WorkflowActivityTrace.Activity.TraceEvent(TraceEventType.Information, 0, "Creating follower {0} on initializer {1}", New Object() { (interfaceType.Name & followermethodName), (interfaceType.Name & initializermethodName) })
                Dim propertyValues As ICollection([Of] CorrelationProperty) = CorrelationResolver.ResolveCorrelationValues(interfaceType, initializermethodName, Nothing, True)
                Dim queueName As New EventQueueName(interfaceType, initializermethodName, propertyValues)
                Dim workflowQueue As WorkflowQueue = Nothing
                If service.Exists(queueName) Then
                    workflowQueue = service.GetWorkflowQueue(queueName)
                Else
                    workflowQueue = service.CreateWorkflowQueue(queueName, True)
                    workflowQueue.Enabled = False
                End If
                workflowQueue.RegisterForQueueItemArrived(eventListener)
            End If
        End Sub

        Private Shared Function GetCorrelationToken(ByVal activity As Activity) As CorrelationToken
            Dim dependencyProperty As DependencyProperty = DependencyProperty.FromName("CorrelationToken", activity.GetType)
            If (dependencyProperty Is Nothing) Then
                dependencyProperty = DependencyProperty.FromName("CorrelationToken", activity.GetType.BaseType)
            End If
            Dim token As CorrelationToken = [TryCast](activity.GetValue(dependencyProperty),CorrelationToken)
            If (token Is Nothing) Then
                Throw New InvalidOperationException(SR.GetString("Error_CorrelationTokenMissing", New Object() { activity.Name }))
            End If
            Dim token2 As CorrelationToken = CorrelationTokenCollection.GetCorrelationToken(activity, token.Name, token.OwnerActivityName)
            If (token2 Is Nothing) Then
                Throw New InvalidOperationException(SR.GetString("Error_CorrelationTokenMissing", New Object() { activity.Name }))
            End If
            Return token2
        End Function

        Friend Shared Sub Initialize(ByVal context As IServiceProvider, ByVal activity As Activity, ByVal interfaceType As Type, ByVal methodName As String, ByVal instanceId As Guid)
            If (activity Is Nothing) Then
                Throw New ArgumentNullException("activity")
            End If
            If (interfaceType Is Nothing) Then
                Throw New ArgumentNullException("interfaceType")
            End If
            If (methodName Is Nothing) Then
                Throw New ArgumentNullException("methodName")
            End If
            CorrelationService.Subscribe(context, activity, interfaceType, methodName, Nothing, instanceId)
            CorrelationService.InitializeFollowers(context, interfaceType, methodName)
        End Sub

        Private Shared Sub InitializeFollowers(ByVal context As IServiceProvider, ByVal interfaceType As Type, ByVal followermethodName As String)
            If Not CorrelationResolver.IsInitializingMember(interfaceType, followermethodName, Nothing) Then
                Dim info As EventInfo
                For Each info In interfaceType.GetEvents
                    CorrelationService.CreateFollowerEntry(context, interfaceType, followermethodName, info.Name)
                Next
            End If
        End Sub

        Friend Shared Sub InvalidateCorrelationToken(ByVal activity As Activity, ByVal interfaceType As Type, ByVal methodName As String, ByVal messageArgs As Object())
            If Not TypeOf CorrelationResolver.GetCorrelationProvider(interfaceType) Is NonCorrelatedProvider Then
                Dim correlationToken As CorrelationToken = CorrelationService.GetCorrelationToken(activity)
                Dim followerProperties As ICollection([Of] CorrelationProperty) = CorrelationResolver.ResolveCorrelationValues(interfaceType, methodName, messageArgs, False)
                If Not CorrelationResolver.IsInitializingMember(interfaceType, methodName, messageArgs) Then
                    If Not correlationToken.Initialized Then
                        Throw New InvalidOperationException(SR.GetString("Error_CorrelationNotInitialized", New Object() { correlationToken.Name, activity.QualifiedName }))
                    End If
                    CorrelationService.ValidateCorrelation(correlationToken.Properties, followerProperties, correlationToken.Name, activity)
                Else
                    correlationToken.Initialize(activity, followerProperties)
                End If
            End If
        End Sub

        Friend Shared Function ResolveQueueName(ByVal activity As Activity, ByVal interfaceType As Type, ByVal methodName As String) As IComparable
            If (activity Is Nothing) Then
                Throw New ArgumentNullException("activity")
            End If
            If (interfaceType Is Nothing) Then
                Throw New ArgumentNullException("interfaceType")
            End If
            If (methodName Is Nothing) Then
                Throw New ArgumentNullException("methodName")
            End If
            If CorrelationResolver.IsInitializingMember(interfaceType, methodName, Nothing) Then
                Return New EventQueueName(interfaceType, methodName, CorrelationResolver.ResolveCorrelationValues(interfaceType, methodName, Nothing, True))
            End If
            Dim correlationToken As CorrelationToken = CorrelationService.GetCorrelationToken(activity)
            If Not correlationToken.Initialized Then
                Return Nothing
            End If
            Return New EventQueueName(interfaceType, methodName, correlationToken.Properties)
        End Function

        Friend Shared Function Subscribe(ByVal context As IServiceProvider, ByVal activity As Activity, ByVal interfaceType As Type, ByVal methodName As String, ByVal eventListener As IActivityEventListener([Of] QueueEventArgs), ByVal instanceId As Guid) As Boolean
            If (activity Is Nothing) Then
                Throw New ArgumentNullException("activity")
            End If
            If (interfaceType Is Nothing) Then
                Throw New ArgumentNullException("interfaceType")
            End If
            If (methodName Is Nothing) Then
                Throw New ArgumentNullException("methodName")
            End If
            Dim service As WorkflowQueuingService = DirectCast(context.GetService(GetType(WorkflowQueuingService)), WorkflowQueuingService)
            Dim queueName As IComparable = CorrelationService.ResolveQueueName(activity, interfaceType, methodName)
            If (Not queueName Is Nothing) Then
                Dim workflowQueue As WorkflowQueue = Nothing
                If service.Exists(queueName) Then
                    workflowQueue = service.GetWorkflowQueue(queueName)
                    workflowQueue.Enabled = True
                Else
                    workflowQueue = service.CreateWorkflowQueue(queueName, True)
                End If
                If (Not eventListener Is Nothing) Then
                    workflowQueue.RegisterForQueueItemAvailable(eventListener, activity.QualifiedName)
                    WorkflowActivityTrace.Activity.TraceEvent(TraceEventType.Information, 0, "CorrelationService: activity '{0}' subscribing to QueueItemAvailable", New Object() { activity.QualifiedName })
                    Return True
                End If
                Return False
            End If
            CorrelationService.SubscribeForCorrelationTokenInvalidation(activity, interfaceType, methodName, eventListener, instanceId)
            Return False
        End Function

        Private Shared Sub SubscribeForCorrelationTokenInvalidation(ByVal activity As Activity, ByVal interfaceType As Type, ByVal followermethodName As String, ByVal eventListener As IActivityEventListener([Of] QueueEventArgs), ByVal instanceId As Guid)
            Dim correlationToken As CorrelationToken = CorrelationService.GetCorrelationToken(activity)
            Dim dataChangeListener As New CorrelationTokenInvalidatedHandler(interfaceType, followermethodName, eventListener, instanceId)
            correlationToken.SubscribeForCorrelationTokenInitializedEvent(activity, dataChangeListener)
        End Sub

        Friend Shared Sub UninitializeFollowers(ByVal interfaceType As Type, ByVal initializer As String, ByVal initializerQueue As WorkflowQueue)
            If CorrelationResolver.IsInitializingMember(interfaceType, initializer, Nothing) Then
                Dim info As EventInfo
                For Each info In interfaceType.GetEvents
                    Dim name As String = info.Name
                    If Not CorrelationResolver.IsInitializingMember(interfaceType, info.Name, Nothing) Then
                        initializerQueue.UnregisterForQueueItemArrived(New FollowerQueueCreator(name))
                    End If
                Next
            End If
        End Sub

        Friend Shared Function Unsubscribe(ByVal context As IServiceProvider, ByVal activity As Activity, ByVal interfaceType As Type, ByVal methodName As String, ByVal eventListener As IActivityEventListener([Of] QueueEventArgs)) As Boolean
            If (activity Is Nothing) Then
                Throw New ArgumentException("activity")
            End If
            If (interfaceType Is Nothing) Then
                Throw New ArgumentNullException("interfaceType")
            End If
            If (methodName Is Nothing) Then
                Throw New ArgumentNullException("methodName")
            End If
            Dim service As WorkflowQueuingService = DirectCast(context.GetService(GetType(WorkflowQueuingService)), WorkflowQueuingService)
            Dim queueName As IComparable = CorrelationService.ResolveQueueName(activity, interfaceType, methodName)
            If ((Not queueName Is Nothing) AndAlso service.Exists(queueName)) Then
                service.GetWorkflowQueue(queueName).UnregisterForQueueItemAvailable(eventListener)
                Return True
            End If
            Return False
        End Function

        Private Shared Sub ValidateCorrelation(ByVal initializerProperties As ICollection([Of] CorrelationProperty), ByVal followerProperties As ICollection([Of] CorrelationProperty), ByVal memberName As String, ByVal activity As Activity)
            If ((Not followerProperties Is Nothing) OrElse (Not initializerProperties Is Nothing)) Then
                If ((followerProperties Is Nothing) OrElse (initializerProperties Is Nothing)) Then
                    Throw New InvalidOperationException(SR.GetString("Error_CorrelationViolationException", New Object() { memberName, activity.QualifiedName }))
                End If
                If (initializerProperties.Count <> followerProperties.Count) Then
                    Throw New InvalidOperationException(SR.GetString("Error_CorrelationViolationException", New Object() { memberName, activity.QualifiedName }))
                End If
                Dim enumerator As IEnumerator([Of] CorrelationProperty) = initializerProperties.GetEnumerator
                Dim enumerator2 As IEnumerator([Of] CorrelationProperty) = followerProperties.GetEnumerator
                Do While (enumerator.MoveNext AndAlso enumerator2.MoveNext)
                    Dim comparable As IComparable = [TryCast](enumerator.Current.Value,IComparable)
                    Dim obj2 As Object = enumerator2.Current.Value
                    If ((Not comparable Is Nothing) AndAlso (comparable.CompareTo(obj2) <> 0)) Then
                        Throw New InvalidOperationException(SR.GetString("Error_CorrelationViolationException", New Object() { memberName, activity.QualifiedName }))
                    End If
                    If ((enumerator.Current.Value Is Nothing) AndAlso (obj2 Is Nothing)) Then
                        Return
                    End If
                    If (((comparable Is Nothing) AndAlso (Not obj2 Is Nothing)) AndAlso Not obj2.Equals(enumerator.Current.Value)) Then
                        Throw New InvalidOperationException(SR.GetString("Error_CorrelationViolationException", New Object() { memberName, activity.QualifiedName }))
                    End If
                Loop
            End If
        End Sub

    End Class
End Namespace

